package com.pragmatic.cryptography;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.pragmatic.domain.CommonRequest;
import com.pragmatic.domain.CommonResponse;
import com.pragmatic.util.HttpClientUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.UUID;

/**
 * SDK工具类(这里当成一个bean)
 *
 * @author wangdong
 * @since 2025/4/22 11:24
 */
@Slf4j
@Component
public class SDKClient {

  // 合作方RSA公钥(用于加密)
  @Value("${partner.public.key}")
  private String partnerPublicKey;

  // 己方RSA私钥(用于签名)
  @Value("${owner.private.key}")
  private String ownerPrivateKey;

  /**
   * 定义泛型处理接口
   *
   * @param request 请求参数对象
   * @param clazz 输出结果对象
   * @param <IN> 入参class
   * @param <OUT> 出参class
   * @return 返回对象
   */
  public <IN, OUT> CommonResponse<OUT> execute(CommonRequest<IN> request, Class<OUT> clazz) {
    // 获取入参对象：加密、加签
    String objRequestJson = JSON.toJSONString(request.getPayload());
    log.info("sdk-请求参数-明文====={}", objRequestJson);
    EncryptMessage encryptMessage = doEncrypt(objRequestJson);
    log.info("sdk-请求参数-密文====={}", JSON.toJSONString(encryptMessage));
    String responseString =
            HttpClientUtil.sendPostJsonStr(
                    request.getRequestUrl(), JSON.toJSONString(encryptMessage), 10000);
    log.info("sdk-响应结果-密文====={}", JSON.toJSONString(encryptMessage));
    // 得到响应结果：验签、解密
    String strResponseJson = doDecrypt(responseString);
    log.info("sdk-响应结果-明文文====={}", strResponseJson);
    // 封装泛型对象
    CommonResponse<OUT> commonResponse =
            JSON.parseObject(strResponseJson, new TypeReference<CommonResponse<OUT>>() {});
    commonResponse.setData(JSON.parseObject(JSON.toJSONString(commonResponse.getData()), clazz));
    // 返回结果
    return commonResponse;
  }

  // ==============================================================================
  // ===========================处理请求对象(加密加签)================================
  // ==============================================================================

  /**
   * 创建加密对象
   *
   * @param content 需加密内容
   * @return EncryptMessage 加密对象
   */
  public EncryptMessage doEncrypt(String content) {
    // 1、随机生成AES对称秘钥
    String aseKey = AESUtil.randomSecretKey();
    // 2、使用对方RSA公钥对AES秘钥加密
    String secretKey = RSAUtil.encryptByPublic(aseKey, partnerPublicKey);
    // 3、使用AES对称秘钥对传输内容进行加密
    String contentEncrypt = AESUtil.encrypt(content, aseKey);
    // 构建传输对象
    EncryptMessage message = new EncryptMessage();
    message.setRequestId(UUID.randomUUID().toString());
    message.setSecretKey(secretKey);
    message.setData(contentEncrypt);
    // 4、使用己方RSA私钥加签
    String rsaSign = RSAUtil.rsaSign(EncryptMessage.objectToStr(message), ownerPrivateKey);
    message.setSign(rsaSign);
    // 返回结果
    return message;
  }

  // ==============================================================================
  // ===========================处理响应对象(验签解密)================================
  // ==============================================================================

  /**
   * 解密密文
   *
   * @param encryptMessageStr 需解密内容
   * @return 解密内容(一般是对象json)
   */
  public String doDecrypt(String encryptMessageStr) {
    try {
      // 转成json对象
      EncryptMessage message = JSON.parseObject(encryptMessageStr, EncryptMessage.class);
      // 1、验签（保证数据未被篡改）
      String sign = message.getSign();
      boolean verifyBool =
          RSAUtil.rsaVerify(EncryptMessage.objectToStr(message), partnerPublicKey, sign);
      if (!verifyBool) throw new RuntimeException("RSA验签异常");
      // 2、解密密文
      // 获取本次请求的aes对称秘钥(被加密过)
      String secretKey = message.getSecretKey();
      // 通过己方私钥解密获取aes加密串
      String aesKey = RSAUtil.decryptByPrivate(secretKey, ownerPrivateKey);
      // 解密密文
      return AESUtil.decrypt(message.getData(), aesKey);
    } catch (Exception e) {
      // 解密异常，直接返回异常状态码给客户端，结束流程
      throw new RuntimeException("解密异常");
    }
  }

  // ============================================================================================

  // 生成秘钥对
  public static void main(String[] args) throws Exception {
    RSAUtil.initKey();
  }
}
